package com.googlecode.proxjed;

import com.google.common.base.Predicate;
import com.googlecode.recycled.lang.assertions.Assert;
import com.googlecode.recycled.lang.jse.api.concurrent.AsyncCaller;
import com.googlecode.recycled.lang.jse.api.concurrent.AsyncResult;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

public class AsyncProxy<T> extends Proxy<T> {

    protected final AsyncCaller asyncCaller;

    public AsyncProxy(T instance, Class<T> type, AsyncCaller asyncCaller, Predicate<Method> joinPoint) {
        super(instance, type, joinPoint);
        this.asyncCaller = Assert.notNull(asyncCaller);
    }

    @Override
    @SuppressWarnings("unchecked")
    public Object doIntercept(T o, final Method method, final Object[] objects, MethodProxy methodProxy) throws Throwable {
        final AsyncResult<?> asyncCallResult = asyncCaller.call(new Callable<Object>() {
            public Object call() throws Exception {
                return method.invoke(instance, objects);
            }
        });
        return Proxies.proxy(new Proxy(asyncCallResult, method.getReturnType(), null) {
            @Override
            public Object doIntercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                return method.invoke(((AsyncResult<?>) instance).get(), objects);
            }
        });
    }

}
